# -*-coding:utf-8 -*-
# @Time: 2023/3/15 19:26
# @Author: cuishuohao
# @File: 四合一
# @Software: PyCharm

# 1.构建回归问题数据集
import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots(2, 2)

np.random.seed(13781)
X = np.random.uniform(-10, 10, 100)  # 100个x采样点
noise = np.random.normal(0, 1, 100)  # 定义噪声满足N(,1)的正态分布
y = 1.5 * X + 0.3 + noise  # 100个点的y值
# y = 1.5 * X + 0.3  # 100个点的y值
ax[0][0].scatter(X, y, label="All 100 data")  # 绘制原始数据的散点图
ax[0][0].set_title("subplot 1")
ax[0][0].legend()

X = X.reshape(-1, 1)  # 增加一个维度
y = y.reshape(-1, 1)  # 增加一个维度
DataSet = np.concatenate((X, y), axis=1)  # 构建数据集

np.random.shuffle(DataSet)  # 随机打乱该数组
TrainSet = DataSet[:80]  # 取前80个样本为训练集
TestSet = DataSet[80:]  # 取后20个样本为测试集
ax[0][1].scatter(TrainSet[:, 0], TrainSet[:, 1], c="blue", label="train data")  # 绘制测试集的散点图
ax[0][1].scatter(TestSet[:, 0], TestSet[:, 1], c="red", label="test data")  # 绘制训练集的散点图
ax[0][1].set_title("subplot 2")
ax[0][1].legend()


def mse(w, b, dataset):
    # 根据模型当前的参数w和b计算模型在数据集dataset上的损失函数
    # w:权重
    # b:偏置值21.
    # dataset:数据集
    # 返回mse
    loss_sum = 0.0  # 初始化loss
    for i in range(len(dataset)):
        x = dataset[i, 0]  # 取得第i个样本的特征(x值)
        y = dataset[i, 1]  # 取得第i个样本的标签(y值)
        f = w * x + b  # 线性回归模型
        loss_sum = loss_sum + (y - f) ** 2  # 所有样本的误差平方和
    mse = loss_sum / len(dataset)  # 对所有样本的误差平方和求平均得到均方误差
    return mse


def gradient_update(w, b, dataset, alpha):
    # 计算当前模型在数据集dataset上的mse后更新模型的参数值
    # w:权重
    # b:偏置值
    # dataset:数据集
    # alpha:学习率
    # 返回w_update,b_update
    N = len(dataset)  # 样本的总数量
    for i in range(N):
        x = dataset[i, 0]  # 取得第i个样本的特征(x值)
        y = dataset[i, 1]  # 取得第i个样本的标签(y值)
        # 计算w和b的梯度
        w_gradient = (2 / N) * (w * x + b - y) * x  # 公式3.17
        b_gradient = (2 / N) * (w * x + b - y)  # 公式3.18
        # 根据梯度下降法，公式3.15,更新参数

        # 我在这里修改了老师的三行代码
    #     w_update = w - alpha * w_gradient
    #     b_update = b - alpha * b_gradient
    # return w_update, b_update
        w = w - alpha * w_gradient
        b = b - alpha * b_gradient
    return w, b


w = np.random.rand()  # w和b的初始值取随机值
b = np.random.rand()
epochs = 10000  # 定义循环次数。老师给的是10000，我这里改成了100
alpha = 0.001  # 定义学习率
loss_mse = []  # 定义一个数组用于保存mse
for step in range(epochs):
    w, b = gradient_update(w, b, TrainSet, alpha)  # 更新梯度
    loss_mse.append(mse(w, b, TrainSet))
    print("训练完成，w:", w, "b:", b)

print()
print("max loss is ", np.max(loss_mse))
print("mean loss is ", np.mean(loss_mse))
print("min loss is ", np.min(loss_mse))
print("finally loss is ", loss_mse[len(loss_mse) - 1])

ax[1][0].scatter(TrainSet[:, 0], TrainSet[:, 1], c="blue", label="train data")
ax[1][0].scatter(TestSet[:, 0], TestSet[:, 1], c="red", label="test data")
ax[1][0].plot(DataSet[:, 0], DataSet[:, 0] * w + b, color="black", label="fitting line")
ax[1][0].set_title("subplot 3")
ax[1][0].legend()

ax[1][1].plot(range(len(loss_mse)), loss_mse, label="Loss change")
plt.xlabel("Epochs")
plt.ylabel("MSE")
ax[1][1].set_title("subplot 4")
ax[1][1].legend()
plt.show()
